#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/syscall.h>
#include <linux/afscall.h>

#define afs_call(function, ...)	(syscall(SYS_afs_syscall, (function), __VA_ARGS__))

struct ViceIoctl vioc;

static inline long pioctl(const char *path,
			  int cmd,
			  struct ViceIoctl *arg,
			  int follow)
{
	int ret = afs_call(AFSCALL_PIOCTL, path, cmd, arg, follow);
	
	if (ret < 0) {
		perror(path);
		exit(1);
	}

	return ret;
}

int arg3(int argc, char *argv[])
{
	if (argc != 3) {
		fprintf(stderr, "Format: %s <opt-id> <file>\n", argv[0]);
		exit(1);
	}
	return 0;
}

/*
 * Test the pioctl - PGetFileCell
*/	
int TestPgetfilecell(char *argv[])
{
	char buffer[256];
	long ret;
	int loop;

	memset(buffer, 0x5a, sizeof(buffer));

        vioc.in = NULL;
        vioc.in_size = 0;
        vioc.out = buffer;
        vioc.out_size = sizeof(buffer);

        ret = pioctl(argv[2], PGetFileCell, &vioc, 1);
        if (ret < 0) {
                perror(argv[2]);
                return 1;
        }

        printf("success: %hd\n", vioc.out_size);
        printf("reply:%s.\n",vioc.out);
        printf("reply:");
        for (loop = 0; loop < vioc.out_size; loop++) {
                printf("%02x", (uint8_t) buffer[loop]);
                if (loop % 4 == 3)
                        printf(" ");
        }
        printf("\n");
	return 0;
}

/*
 * Test the pioctl - PGetFID
 */
int TestPgetfid(char *argv[])
{
	char buffer[256];
        long ret;
        int loop;

        /* poison the reply buffer before letting AFS see it */
        memset(buffer, 0x5a, sizeof(buffer));

        vioc.in = NULL;
        vioc.in_size = 0;
        vioc.out = buffer;
        vioc.out_size = sizeof(buffer);

        ret = pioctl(argv[2], PGetFID, &vioc, 1);
        if (ret < 0) {
                perror(argv[2]);
                return 1;
        }

        printf("success: %hd.\n", vioc.out_size);
        printf("reply:");
        for (loop = 0; loop < vioc.out_size; loop++) {
                printf("%02x", (uint8_t) buffer[loop]);
                if (loop % 4 == 3)
                        printf(" ");
        }
        printf("\n");
        return 0;
}

/*
 * Test the pioctl - PGetVolStat
 */
int TestPgetvolstat(char *argv[])
{
	struct VolumeStatus *VlStat;
	char *buffer;
	int loop;
	int ret;

	vioc.out_size = sizeof(struct VolumeStatus);
	vioc.out = malloc(sizeof(struct VolumeStatus));

        ret = pioctl(argv[2], PGetVolStat, &vioc, 1);
        if (ret < 0) {
                perror(argv[2]);
                return 1;
	}
	
	VlStat = (struct VolumeStatus *)vioc.out;
	buffer = (char *)vioc.out;

	printf("PGetVolStat Success:\tvid %d,\tPid %d,\tonline %x,\tInsev %x,\n"
		"\tBlessed %x,\tNeedsal %x,\tType %d, \tMinQ %d,\n"
		"\tMaxQ %d,\tBlockinuse %d,\tPartBlocksA %d,\tPartMaxB %d.\n\n",
		VlStat->Vid, VlStat->ParentId, VlStat->Online, VlStat->InService,
		VlStat->Blessed, VlStat->NeedsSalvage, VlStat->Type, VlStat->MinQuota,
		VlStat->MaxQuota, VlStat->BlocksInUse, VlStat->PartBlocksAvail,
		VlStat->PartMaxBlocks);

	printf("reply:");
        for (loop = 0; loop < vioc.out_size; loop++) {
                printf("%02x", (uint8_t) buffer[loop]);
                if (loop % 4 == 3)
                        printf(" ");
        }
        printf("\n");
	free(vioc.out);
        return 0;
}
/*
 * Test the pioctl - PWhereIs
 */
int TestPWhereIs(char *argv[])
{
	int i, ret, loop;
	int addr[AFS_MAXHOSTS];

	vioc.out_size = AFS_MAXHOSTS * sizeof(int);
	vioc.out = (char*)addr;

        ret = pioctl(argv[2], PWhereIs, &vioc, 1);
        if (ret < 0) {
                perror(argv[2]);
                return 1;
	}

	printf("PWhereIs success:\n");
	for(i = 0; i < vioc.out_size / sizeof(int); i++) {
		 printf("\tIP Address %d : %s\n",i,inet_ntoa(*((struct in_addr *)&addr[i])));
	}

	printf("reply:");
        for (loop = 0; loop < vioc.out_size; loop++) {
                printf("%02x", (uint8_t) ((char*)addr)[loop]);
                if (loop % 4 == 3)
                        printf(" ");
        }
        printf("\n");
	return 0;
}

/*
 * print the supported operation-id
 */
int printhelp()
{
	printf("Operation-id supproted:\n");
	printf(
		"\t%d\t\t%s\n"
		"\t%d\t\t%s\n"
		"\t%d\t\t%s\n"
		"\t%d\t\t%s\n"
		"\t%d\t\t%s\n"
		"Opt count is %d.\n",

		PGetVolStat, "PGetVolStat",
		PWhereIs, "PWhereIs",
		PGetFID, "PGetFID",
		PNewCell, "PNewCell",
		PGetFileCell, "PGetFileCell",
		4);
	return 0;
}

int main(int argc, char *argv[])
{
	int loop;
	int opt;

	arg3(argc, argv);
	opt = strtol(argv[1], NULL, 10);
	if(opt < 0) {
		printf("ERROR: %s.\n", strerror(errno));
		return -1;
	}

	memset(&vioc, 0x00, sizeof(struct ViceIoctl));	

	switch (opt) {
		case PGetFileCell:
			return TestPgetfilecell(argv);
		case PGetFID:
			return TestPgetfid(argv);
		case PGetVolStat:
			return TestPgetvolstat(argv);
		case PNewCell:
			return pioctl(argv[2], PNewCell, &vioc, 1);
		case PWhereIs:
			return TestPWhereIs(argv);
		default:
			printf("Operation-id [%d] is not supproted by the test_object.\n", opt);
			return printhelp();	
	}

	return 0;

}
